package com.example.block_chain.RabbitMQ;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.example.block_chain.domain.pbft.VoteEnum;
import com.example.block_chain.domain.pbft.VoteInfo;
import com.example.block_chain.domain.pojo.Block;
import com.example.block_chain.service.blockChain.BlockService;
import com.example.block_chain.utils.MerkelTreeNodeUtil;
import com.example.block_chain.utils.SimpleMerkleTree;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/*
 *@description: 消息接收
 *@author: hisen
 *@Time: 2023/11/22  21:05
 */
@Component("schoolConsumer")
@Slf4j
public class SchoolConsumer {
    @Value("${pbft.node-count}")
    private Integer nodeCount;
    @Value("${pbft.target-count}")
    private Integer targetCount;
    @Resource
    Producer producer;

    @Resource
    BlockService blockService;

    private final Map<String, Integer> blockVoteMap = new HashMap<>();

    @RabbitListener(queues = "schoolAQueue")
    public void handleMessage(VoteInfo voteInfo) {
        log.info("收到服务端的消息：{}", voteInfo);
        if (voteInfo.getCode() == VoteEnum.REQUEST.getCode()) {
            voteInfo.setCode(VoteEnum.PRE_PREPARE.getCode());
            producer.sendMessage(voteInfo);
            return;
        }
        if (voteInfo.getCode() == VoteEnum.PRE_PREPARE.getCode()) {
            voteInfo.setCode(VoteEnum.PREPARE.getCode());
            producer.sendMessage(voteInfo);
            return;
        }
        if (voteInfo.getCode() == VoteEnum.PREPARE.getCode()) {
            voteInfo.setCode(VoteEnum.COMMIT.getCode());
            producer.sendMessage(voteInfo);
            return;
        }
        if (voteInfo.getCode() == VoteEnum.COMMIT.getCode()) {
            //校验hash
            Block block = voteInfo.getBlock();
            String blockHash = block.getMerkleHash();
            List<String> list = MerkelTreeNodeUtil.getTreeNodeList(block);
            if (Strings.isBlank(blockHash) || !blockHash.equals(SimpleMerkleTree.getTreeNodeHash(list))) {
                log.info("区块信息有误！");
                return;
            }
            log.info("获取1票！");
            if (commitSuccess(blockHash)) {
                //学生区块则判断是否更新父区块的之区块hash字段
                if (block.getType() == 4 && voteInfo.getId() != null) {
                    LambdaUpdateWrapper<Block> wrapper = new LambdaUpdateWrapper<>();
                    wrapper.set(Block::getNextHash, block.getMerkleHash());
                    wrapper.eq(Block::getId, voteInfo.getId());
                    blockService.update(wrapper);//更新父区块的子区块hash值
                }
                log.info("当前区块：" + block);
                blockService.save(block);//区块链上链
            }
        }
    }

    private boolean commitSuccess(String blockHash) {
        int count = blockVoteMap.getOrDefault(blockHash, 0) + 1;
        blockVoteMap.put(blockHash, count);
        if (count >= targetCount) {
            blockVoteMap.remove(blockHash);
            return true;
        }
        return false;
    }
}
